using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Reflection;
using System.Web.UI;
using System.Text.RegularExpressions;
using Rotempco.Core.ORM.Logic;
using System.Collections;
using Rotempco.Core.Web.Objects.Stores;
using Rotempco.Core.Web.UserControls;
using Rotempco.Core.Web.Objects.Enums;
using Rotempco.Core.Web.Objects;

namespace Rotempco.Core.Web.Logic
{
    public class WebFormFiller
    {
        private static List<Type> _ControlTypes;

        private static List<Type> ControlTypes
        {
            get
            {
                if (_ControlTypes == null)
                {
                    _ControlTypes = new List<Type>();
                    _ControlTypes.Add(typeof(TextBox));
                    _ControlTypes.Add(typeof(Literal));
                    _ControlTypes.Add(typeof(Label));
                    _ControlTypes.Add(typeof(CheckBox));
                    _ControlTypes.Add(typeof(RadioButton));
                    _ControlTypes.Add(typeof(DropDownList));
                    _ControlTypes.Add(typeof(Repeater));
                    _ControlTypes.Add(typeof(HiddenField));
                    _ControlTypes.Add(typeof(HyperLink));
                    _ControlTypes.Add(typeof(PlaceHolder));
                    _ControlTypes.Add(typeof(Image));
                    _ControlTypes.Add(typeof(ControlX));
                }

                return _ControlTypes;
            }
        }

        #region [ Public ]

        public static void FillForm<T>(T instance, Control container)
        {
            if (instance == null)
                return;

            PropertyInfo[] properties = typeof(T).GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                Control control = LocateControl(container, ControlTypes, propertyInfo.Name);

                if (control == null || control.Parent is RepeaterItem)
                    continue;

                SetControlValue(control, instance, propertyInfo);
            }
        }

        public static void FillObject<T>(T instance, Control container)
        {
            PropertyInfo[] properties = typeof(T).GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                Control control = LocateControl(container, ControlTypes, propertyInfo.Name);

                if (control == null || control.Parent is RepeaterItem)
                    continue;

                SetInstanceValue<T>(instance, propertyInfo, control);
            }
        }

        public static void FillRepeater<T>(List<T> items, Repeater repeater)
        {
            FillRepeaterFromObjects(items.Cast<object>().ToList(), repeater);
        }

        public static T FillObject<T>(Control container) where T : class, new()
        {
            T instance = new T();
            FillObject<T>(instance, container);
            return instance;
        }

        public static void FillObjects<T>(List<T> items, Repeater repeater)
        {
            FillObjectsFromRepeater(ConvertPropertyToObjectList(items), repeater);
        }

        #endregion

        private static void SetControlValue(Control control, object instance, PropertyInfo propertyInfo)
        {
            object propertyValue = GetPropertyValue(control.ID, propertyInfo, instance);

            string textValue;
            if (!string.IsNullOrWhiteSpace(WebStore.Instance.DefaultWebDateFormat) && (propertyInfo.PropertyType == typeof(DateTime?) || propertyInfo.PropertyType == typeof(DateTime)))
                textValue = String.Format(WebStore.Instance.DefaultWebDateFormat, propertyValue);
            else
                textValue = propertyValue + "";

            if (control is DropDownList)
            {
                // TODO: Ask Philip: What is correct:
                // Original:
                //object enumThingy = (int)propertyInfo.GetValue(instance, null);
                //(control as DropDownList).SelectedValue = enumThingy + "";

                // New:
                if (!string.IsNullOrWhiteSpace(textValue))
                {
                    //if (propertyInfo.PropertyType.IsEnum)
                    //{
                    //    object enumThingy = (int)propertyInfo.GetValue(instance, null);
                    //    (control as DropDownList).SelectedValue = enumThingy + "";
                    //}
                    //else
                    (control as DropDownList).SelectedValue = textValue + "";
                }
            }
            else if (control is ITextControl)
                (control as ITextControl).Text = textValue;
            else if (control is ICheckBoxControl)
                (control as ICheckBoxControl).Checked = bool.Parse(textValue);
            else if (control is Repeater)
            {
                List<object> propertyValueList = ConvertPropertyToObjectList(propertyValue);
                FillRepeaterFromObjects(propertyValueList, (control as Repeater));
            }
            else if (control is HiddenField)
                (control as HiddenField).Value = textValue;
            else if (control is HyperLink)
                (control as HyperLink).Text = textValue;
            else if (control is PlaceHolder)
            {
                Control controlX = (Control)propertyValue;
                if (controlX != null)
                    (control as PlaceHolder).Controls.Add(controlX);
            }
            else if (control is Image)
            {
                Image image = control as Image;
                if (string.IsNullOrWhiteSpace(textValue))
                    image.Visible = false;
                else
                {
                    image.ImageUrl = textValue;
                    if (WebStore.Instance.AddWithAndHeightToImageUrl)
                    {
                        if (image.Width.IsEmpty || image.Height.IsEmpty)
                            return;

                        if (!image.ImageUrl.Contains("?"))
                            image.ImageUrl += "?";
                        else
                            image.ImageUrl += "&";

                        image.ImageUrl += "width=" + image.Width.Value + "&height=" + image.Height.Value;
                    }
                }
            }
            else if (control is ControlX)
            {
                ControlX c = control as ControlX;

                object fieldTypeValue = instance.GetType().GetProperty("FieldType").GetValue(instance, null);
                FieldType fieldType = (FieldType)Enum.Parse(typeof(FieldType), fieldTypeValue.ToString()); 

                string fieldValue = instance.GetType().GetProperty(c.FieldValuePropertyName).GetValue(instance, null) + "";
                string fieldOptions = instance.GetType().GetProperty(c.FieldOptionsPropertyName).GetValue(instance, null) + "";

                c.FieldType = fieldType;
                c.FieldValue = fieldValue;
                c.FieldOptions = fieldOptions;
            }
        }

        private static object GetPropertyValue(string controlName, PropertyInfo propertyInfo, object instance)
        {
            if (!controlName.Contains("__"))
            {
                return propertyInfo.GetValue(instance, null);
            }
            else
            {
                string[] controlElements = Regex.Split(controlName, "__");
                object obj = propertyInfo.GetValue(instance, null);
                for (int i = 0; i < controlElements.Length - 1; i++)
                {
                    propertyInfo = obj.GetType().GetProperty(controlElements[i + 1]);
                    obj = propertyInfo.GetValue(obj, null);
                }

                return obj;
            }
        }

        private static void SetInstanceValue<T>(T instance, PropertyInfo propertyInfo, Control control)
        {
            object propertyValue = GetPropertyValueFromControl(control, propertyInfo);

            if (control is DropDownList)
                FillObjectFromDropDownList<T>(instance, propertyInfo, propertyValue);
            else if (control is Repeater)
                FillObjectsFromRepeater<T>(instance, propertyInfo, control as Repeater);
            else if (control is ControlX)
            {
                ControlX c = (ControlX)control;

                instance.GetType().GetProperty("FieldType").SetValue(instance, c.FieldType,null);
                instance.GetType().GetProperty(c.FieldValuePropertyName).SetValue(instance, c.FieldValue, null);
                instance.GetType().GetProperty(c.FieldOptionsPropertyName).SetValue(instance, c.FieldOptions, null);
            }
            else
                SetInstanceValue(control.ID, instance, propertyInfo, propertyValue);
        }

        private static void SetInstanceValue(string controlName, object instance, PropertyInfo propertyInfo, object propertyValue)
        {
            string[] controlElements = Regex.Split(controlName, "__");
            if (controlElements.Count() > 1)
            {
                object obj = propertyInfo.GetValue(instance, null);
                for (int i = 0; i < controlElements.Length - 1; i++)
                {
                    propertyInfo = obj.GetType().GetProperty(controlElements[i + 1]);
                    if (i < controlElements.Length - 2)
                        obj = propertyInfo.GetValue(obj, null);
                }

                propertyInfo.SetValue(obj, propertyValue, null);
            }
            else if (propertyInfo.PropertyType.BaseType == typeof(Enum))
                propertyInfo.SetValue(instance, Enum.Parse(propertyInfo.PropertyType, propertyValue.ToString()), null);
            else
                propertyInfo.SetValue(instance, propertyValue, null);

        }

        private static void FillRepeaterFromObjects(List<object> instanceList, Repeater repeater)
        {
            repeater.DataSource = instanceList;
            repeater.DataBind();

            if (instanceList.Count <= 0)
                return;

            PropertyInfo[] properties = instanceList.First().GetType().GetProperties();

            int i = 0;

            foreach (RepeaterItem repeaterItem in repeater.Controls)
            {
                if (!(repeaterItem.ItemType == ListItemType.Item || repeaterItem.ItemType == ListItemType.AlternatingItem))
                    continue;

                object instance = instanceList[i++];

                foreach (PropertyInfo propertyInfo in properties)
                {
                    Control control = LocateControl(repeaterItem, ControlTypes, propertyInfo.Name);

                    if (control == null)
                        continue;

                    SetControlValue(control, instance, propertyInfo);
                }
            }
        }

        private static void FillObjectsFromRepeater<T>(T instance, PropertyInfo propertyInfo, Repeater repeater)
        {
            object propertyValue = propertyInfo.GetValue(instance, null);
            List<object> propertyValueList = ConvertPropertyToObjectList(propertyValue);
            FillObjectsFromRepeater(propertyValueList, repeater);
        }

        private static void FillObjectsFromRepeater(List<object> items, Repeater repeater)
        {
            if (items.Count == 0)
                return;

            PropertyInfo[] properties = items.First().GetType().GetProperties();

            int i = 0;

            foreach (RepeaterItem repeaterItem in repeater.Items)
            {
                object instance = items[i++];

                foreach (PropertyInfo propertyInfo in properties)
                {
                    Control control = LocateControl(repeaterItem, ControlTypes, propertyInfo.Name);

                    if (control == null || control is Literal)
                        continue;

                    object value = GetPropertyValueFromControl(control, propertyInfo);
                    SetInstanceValue<object>(instance, propertyInfo, control);
                }
            }
        }

        private static void FillObjectFromDropDownList<T>(T instance, PropertyInfo propertyInfo, object propertyValue)
        {
            if (propertyInfo.PropertyType == typeof(string))
                propertyInfo.SetValue(instance, propertyValue + "", null);

            else if (propertyInfo.PropertyType == typeof(int) || propertyInfo.PropertyType == typeof(int?))
                if (propertyValue != null)
                    propertyInfo.SetValue(instance, int.Parse(propertyValue + ""), null);
                else
                    propertyInfo.SetValue(instance, null, null);

            else if (propertyInfo.PropertyType == typeof(long) || propertyInfo.PropertyType == typeof(long?))
            {
                if (propertyValue != null)
                    propertyInfo.SetValue(instance, long.Parse(propertyValue + ""), null);
                else
                    propertyInfo.SetValue(instance, null, null);
            }
            else if (propertyInfo.PropertyType == typeof(double) || propertyInfo.PropertyType == typeof(double?))
            {
                if (propertyValue != null)
                    propertyInfo.SetValue(instance, double.Parse(propertyValue + ""), null);
                else
                    propertyInfo.SetValue(instance, null, null);
            }
            else
                propertyInfo.SetValue(instance, Enum.Parse(propertyInfo.PropertyType, propertyValue + ""), null);
        }

        private static object GetPropertyValueFromControl(Control control, PropertyInfo propertyInfo)
        {
            if (control is DropDownList)
            {
                string selectedValue = (control as DropDownList).SelectedValue;

                if (selectedValue.ToLower() == "null")
                    selectedValue = null;

                return DBNullHelper.ToType(selectedValue, propertyInfo.PropertyType);
            }
            else if (control is ITextControl)
            {
                string text = (control as ITextControl).Text;

                if (string.IsNullOrWhiteSpace(text) && DBNullHelper.IsNullable(propertyInfo))
                    return null;
                else
                    return DBNullHelper.ToType(text, propertyInfo.PropertyType);
            }
            else if (control is ICheckBoxControl)
            {
                bool isChecked = (control as ICheckBoxControl).Checked;
                return DBNullHelper.ToType(isChecked, propertyInfo.PropertyType);
            }
            else if (control is HiddenField)
            {
                string text = (control as HiddenField).Value;

                if (string.IsNullOrWhiteSpace(text) && DBNullHelper.IsNullable(propertyInfo))
                    return null;
                else
                    return DBNullHelper.ToType(text, propertyInfo.PropertyType);
            }
            else if (control is HyperLink)
            {
                string text = (control as HyperLink).Text;

                if (string.IsNullOrWhiteSpace(text) && DBNullHelper.IsNullable(propertyInfo))
                    return null;
                else
                    return DBNullHelper.ToType(text, propertyInfo.PropertyType);
            }
            else if (control is ControlX)
            {
                ControlX c = (control as ControlX);
                return c.FieldValue;
            }

            return null;
        }

        private static Control LocateControl(Control control, List<Type> controlTypes, string propertyName)
        {
            foreach (Type controlType in controlTypes)
            {
                Control returnControl = LocateControl(control, controlType.Name + "_" + propertyName);

                if (returnControl != null)
                    return returnControl;
            }

            return null;
        }

        private static Control LocateControl(Control control, string controlName)
        {
            Control returnControl = null;

            foreach (Control innerControl in control.Controls)
            {
                if (innerControl.ID != null && innerControl.ID.Equals(controlName, StringComparison.InvariantCultureIgnoreCase) && innerControl is Control)
                    returnControl = innerControl;
                else if (innerControl.ID != null && innerControl.ID.StartsWith(controlName, StringComparison.InvariantCultureIgnoreCase) && innerControl.ID.Contains("__"))
                    returnControl = innerControl;
                else
                    returnControl = LocateControl(innerControl, controlName);

                if (returnControl != null)
                    break;
            }

            return returnControl;
        }

        private static List<object> ConvertPropertyToObjectList(object propertyValue)
        {
            if (propertyValue == null)
                return new List<object>();

            List<object> objectList = (propertyValue as IEnumerable).Cast<object>().ToList();
            return objectList;
        }
    }
}
